#ifndef BACKPROPAGATION_H
#define BACKPROPAGATION_H

#include <vector>
#include <string>

#include "rna/network.h"

#ifndef input_t
#define input_t std::pair<data_t*, data_t*>
#endif

class Backpropagation {
public:

    enum Exceptions {
        FileNotFound
    };

    explicit Backpropagation(Network *network, data_t learningRate = .3, data_t momentum = .1);
    ~Backpropagation();

    /**
     * @brief randomize
     */
    void randomize();

    /**
     * @brief setErrorThresh
     * @param errorThresh erro máximo permitido
     */
    void setErrorThresh(data_t errorThresh);

    /**
     * @brief setIterations
     * @param iterations número máximo de iterações
     */
    void setMaxIterations(size_t iterations);

    /**
     * @brief train
     * @param input
     * @param target
     * @return {data_t} error
     */
    data_t train( const input_t &data );

    /**
     * @brief train
     * @param size
     * @param input
     * @param target
     * @return {size_t} iterations
     */
    size_t train( const std::vector< const input_t* > &data );

    /**
     * @brief train
     * @param filename
     * @return {size_t} iterations;
     */
    size_t train(const std::string &filename);

protected:
    Network *_network;
    data_t ***_changes;
    data_t **_biasChanges;

    data_t **_accumulators;
    data_t **_outputs;
    data_t **_errors;

    data_t _momentum;
    data_t _errorThresh;
    data_t _learningRate;
    size_t _iterations;

    data_t calculateErrors(const data_t *target);
    void calculateChanges();
    void forward(const data_t *input);
    data_t backward(data_t *target);

    std::vector<const input_t *> load(const std::string &filename);
    void destroy(std::vector<const input_t*>& data);

};

#endif // BACKPROPAGATION_H
